// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CXFormsParse.
//   $Id$


/* Parse.cpp
 *
 * This module creates objects that parse an incoming XML tag, putting the contents of the
 * attributes and elements into member variables.
 */

#include "stdafx.h"
#include "XFormsApp.h"

#include "Parse.h"
#include "tags.h"

extern CXFormsApp theApp;



CXFormsParse::CXFormsParse(CImpl* pImpl):
	m_psImplContent(0)
	,m_bCheckElements(true)
	{
		addAttributeCheck(ATTR_ID, &pImpl->m_sId);
		addAttributeCheck(ATTR_APPEAR, &pImpl->m_sAppearance);
	};

CXFormsParse::~CXFormsParse()
{

	m_PossibleAttributeList.clear();
	m_CompulsoryAttributeList.clear();
	m_AttributeParseFunctions.clear();
	m_AttributeParseMaps.clear();

}

HRESULT CXFormsParse::Parse(IHTMLElement* pElem, CImpl* pImpl)
{
	HRESULT hr = SetAttributes(pElem, pImpl);
	if(S_OK == hr) {
		hr = SetElements(pElem, pImpl);
		if(m_psImplContent) {
			::SysFreeString(*m_psImplContent);
			*m_psImplContent = 0;
			pElem->get_innerHTML(m_psImplContent);
		}
	}
	return hr;
}

HRESULT CXFormsParse::CheckAttribute( CComBSTR & sName,VARIANT v,LPVOID* pDest)
{
	
	
	if (!(v.vt & (VT_NULL | VT_EMPTY)))
	{
		HRESULT (_stdcall*pFunc)(VARIANT,LPVOID*) = 0;
		CMapBSTRToParseFunction::iterator i = m_AttributeParseFunctions.find(sName);
		if(i != m_AttributeParseFunctions.end())
			pFunc = i->second;
		if(pFunc)
		{
			//We have been provided with a callback to 
			//	use to tokenise the attribute.

			// I had originally thought this could be a map,
			//	but that is not flexible enough, we may need
			//	to perform space-delimited array splitting
			//	or other complex task, so a callback to 
			//	a static member function is provided.
			HRESULT hr = pFunc(v,pDest);
			if(FAILED(hr))
			{
				//We can now throw a parse error if you like.
/*					CComBSTR sError;
				swprintf(sError,_T("unexpected token %s found in attribute %s"),v.bstrVal,sName);
				reportError(sError); //NOTIMPL
*/				return E_FAIL;
			}
		}
		else
		{
			//CMap based processing.
			CMapStringToPtr * theMap = NULL;
			CMapBSTRToStringMap::iterator i = m_AttributeParseMaps.find(sName);
			if(i != m_AttributeParseMaps.end())
				theMap =i->second;
			
			if(theMap)
			{
				LPVOID pLU = NULL;
				BOOL bSuccess = theMap->Lookup(CString(v.bstrVal),(void*&)pLU);
				if(bSuccess)
				{
					*pDest = pLU;
				}
				else
				{
					//We can now throw a parse error if you like.
					return E_FAIL;
				}
			}
			else
			{
				//Default processing, get the string value of the 
				//	attribute
					CComVariant cv(v);
					cv.CopyTo(reinterpret_cast<BSTR*>(pDest));
			}

		}
	}
	return S_OK;
}

HRESULT CXFormsParse::SetAttributes(IHTMLElement *pElem, CImpl* pImpl)
{
	HRESULT hr = S_OK;

	CListCComBSTRLPVoidPair::iterator i = m_CompulsoryAttributeList.begin();
	while(i != m_CompulsoryAttributeList.end())
	{
		CComVariant vtVal;
		hr = pElem->getAttribute(i->first, 1, &vtVal);
		if(FAILED(hr) || vtVal.vt == VT_NULL)
		{
			//TODO:
			//Is this a fatal exception?

			//report missing attribute error.
			CString sErr;
			CComBSTR sTag;
			pElem->get_tagName(&sTag);
			sErr.Format("Compulsory attribute \"%s\" on element \"%S\" missing.", i->first, sTag);
			
			if(pImpl->m_pXForms)
				pImpl->m_pXForms->reportError(sErr);
			else
				::MessageBox(0,sErr,"",0);

			return E_FAIL;
		}
		hr = CheckAttribute(i->first, vtVal, i->second);
		if(FAILED(hr))
		{
			//report unparsable attribute error.
			return E_FAIL;
		}
		++i;
	}

	i =	m_PossibleAttributeList.begin();
	while(i != m_PossibleAttributeList.end())
	{
		CComVariant vtVal;
		hr = pElem->getAttribute(i->first, 3, &vtVal);
		if(vtVal.vt != VT_NULL)
			CheckAttribute(i->first, vtVal,i->second);
		++i;
	}

	return hr;
}


HRESULT CXFormsParse::SetElements(IHTMLElement *pElem, CImpl* pImpl)
{
	HRESULT hr = S_OK;

	/*
	 * Check each of the children of this element
	 */
	if(m_bCheckElements)
	{
		IDispatch* pDispatch = NULL;

		hr = pElem->get_children(&pDispatch);
		if (S_OK == hr)
		{
			CComQIPtr<IHTMLElementCollection> pChildren = pDispatch;
			if(pChildren) {
				long l;

				pChildren->get_length(&l);

				for (long i = 0; i < l; i++)
				{
					IDispatch* pDispTemp = NULL;
					CComVariant index = i;
					if (S_OK == pChildren->item(index, index, &pDispTemp))
					{
						CComQIPtr<IHTMLElement> pElement = pDispTemp;
						if(pElement) {
							CComBSTR bstrTag = NULL;

							pElement->get_tagName(&bstrTag);
							CheckElement(pElement, bstrTag, pImpl);
							bstrTag.Empty();
						}
					}
					if (pDispTemp)
						pDispTemp->Release();
				}//for ( each child element )
			}//if ( we retrieved the child element interface )
		}//if ( there are child elements )
		if (pDispatch)
			pDispatch->Release();
	}

	return hr;
}

void CXFormsParse::CheckElement(IHTMLElement * /*pElem*/, BSTR /*bstrTag*/, CImpl* /*pImpl*/)
{
	/*
	 * This is overridden by classes that inherit from this class, so that they can check for child
	 * nodes specific to them
	 */
}



/*
	could be a template????

template <class TImpl>
	void checkElement(IHTMLElement * pElem,TImpl * pLocation)
{
	TImpl * pImpl = new TImpl();
	
}
*/


/*
 * CXFormsParseFormControl handles the common parsing for all form controls
 */


/*
Removed due to the return value S_FALSE was stopping CImplItem::init() being 
called when parsing a xf:choice which contains a xf:item (CXFormsParseChoice::CheckElement()).
Have checked that CXFormsParseItem::Parse() is not called by any other object.

This fixes bug 239.


HRESULT CXFormsParseItem::Parse(IHTMLElement* pElem, CImpl* pImpl)
{
	HRESULT hr = __super::Parse(pElem,pImpl);
	//HACK: to stop items recieving init(NULL) too early.
	if(SUCCEEDED(hr))
	{
	 	hr = S_FALSE;
	}
	return hr;
}
*/


HRESULT __stdcall XFormsParse::parseXSDDouble(VARIANT vtStr, double *d)
{
	//TODO: generic for different numeric types.
	switch(vtStr.vt)
	{
		case VT_NULL:
		case VT_EMPTY:
			*d = 0;
			return S_FALSE;
		case VT_I4:
			*d = vtStr.lVal;
			return S_OK;
		// TODO: Convert from other numeric types.
		case VT_BSTR:
			*d = _wtof(vtStr.bstrVal);
			return S_OK;
		default:
			return E_FAIL;
	}
}

	HRESULT __stdcall XFormsParse::parseXSDLong(VARIANT vtStr ,LPVOID* vdTok) 
	{
		//TODO: generic for different numeric types.
		switch(vtStr.vt)
		{
			case VT_NULL:
			case VT_EMPTY:
				*vdTok = NULL;
				return S_FALSE;
			case VT_I4:
				*vdTok = (LPVOID)vtStr.lVal;
				return S_OK;
		//TODO: convert from other number types.
			case VT_BSTR:
				*vdTok = (LPVOID)_wtol(vtStr.bstrVal);
				return S_OK;
			default:
				return E_FAIL;
		}
	}

	HRESULT __stdcall XFormsParse::parseXSDBoolean(VARIANT vtStr ,LPVOID* vdTok) 
	{
		//	I don't know if this is all neccessary, or whether 
		//		I can just return boolVal whatever.
		//		I think it is needed, as boolVal might be set
		//		in situations that would otherwise give a parse
		//		Error.

		switch(vtStr.vt)
		{
			case VT_NULL:
				*vdTok = NULL;
				return S_FALSE;
			case VT_BOOL:
				*vdTok = (LPVOID)vtStr.boolVal;
				return S_OK;
			case VT_I4:
				switch(vtStr.intVal)
				{
					case 0:
						*vdTok = (LPVOID)false;
						return S_OK;
					case 1:
						*vdTok = (LPVOID)true;
						return S_OK;
					default:
						return E_FAIL;
				}
			case VT_BSTR:
			{
				if(_wcsicmp(vtStr.bstrVal,L"true")==0)
				{
					*vdTok = (LPVOID)true;
					return S_OK;
				}
				else if(_wcsicmp(vtStr.bstrVal,L"false")==0)
				{
					*vdTok = (LPVOID)false;
					return S_OK;				
				}
				
			}
		}
		return E_FAIL;
	}


